#include "../include/fserver.h"

char rootfilepath[500]; 
bool hiddenfile = false;
int outfilesize = 0;
//int hassend = 0;

bool sendfile(int new_fd, char *path, char * file_name, char *buffer)
{
	printf("fd = %d, file: %s begin send...\n", new_fd, path);
	char data_tmp[512] = {0}; //要发的数据部分内容
	long file_size = get_file_size(path);
	if (-1 == file_size)
	{
		printf("fd = %d, get_file_size err!\n", new_fd);
		return false;
	}
	sprintf(data_tmp, "<file><name>%s</name><size>%ld</size></file>", file_name, file_size); //要发送的数据部分内容

	struct TCP_STRUCT_DATA struct_data; //数据头
	memset(&struct_data, 0x0, sizeof(struct_data));
	struct_data.m_cmd = GET_FILE;			   //获取文件命令
	struct_data.m_data_len = strlen(data_tmp); //数据部分真实的长度

	memset(buffer, 0, BUFFER_SIZE);
	int send_len = sizeof(struct_data);
	memcpy(buffer, &struct_data, send_len);

	//向服务器发送buffer中的数据
	int len = send(new_fd, buffer, send_len, 0); //发数据头部分
	if (len < 0)
	{
		printf("fd = %d, send err...\n", new_fd); //发送失败
		return false;
	}
	memset(buffer, 0, BUFFER_SIZE);
	
	if((len = recv(new_fd, buffer, 6, 0)) < 0)
	{
		printf("fd = %d, recv err...\n", new_fd); //接收失败
		return false;
	}
	if(strncmp(buffer, "#over#", 6))
	{
		printf("fd = %d, recv data is not #over#...\n", new_fd); //接收失败
		return false;
	}
	len = send(new_fd, data_tmp, struct_data.m_data_len, 0);
	if (len < 0)
	{
		printf("fd = %d, send err...\n", new_fd); //发送失败
		return false;
	}

	memset(buffer, 0, BUFFER_SIZE);
	
	if((len = recv(new_fd, buffer, 6, 0)) < 0)
	{
		printf("fd = %d, recv err...\n", new_fd); //接收失败
		return false;
	}
	if(strncmp(buffer, "#over#", 6))
	{
		printf("fd = %d, recv data is not #over#...\n", new_fd); //接收失败
		return false;
	}

	int file2_fp = open(path, O_RDONLY);
	if(file2_fp < 0)
	{
		printf("fd = %d, file: %s open failed.\n", new_fd, path);
		return false;
	}

	int length = 0;
	memset(buffer, 0, BUFFER_SIZE);
	// 每读取一段数据，便将其发送给客户端，循环直到文件读完为止
	int hassend = 0;
	while ((length = read(file2_fp, buffer, BUFFER_SIZE)) > 0)
	{
		if (send(new_fd, buffer, length, MSG_NOSIGNAL) < 0)
		{
			printf("fd = %d, file: %s send failed.\n", new_fd, path);
			close(file2_fp);
			return false;
		}
		hassend += length;
		if(hassend > MAX_SENDLENGTH)
		{
			memset(buffer, 0, BUFFER_SIZE);
			if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
			{
				printf("fd = %d, file: %s recv failed.\n", new_fd, path);
				close(file2_fp);
				return false;
			}
			if(strncmp(buffer, "#beginsend#", 11))
			{
				printf("fd = %d, file: %s #beginsend# recv failed.\n", new_fd, path);
				close(file2_fp);
				return false;
			}
			hassend = 0;
		}
		//printf("send length %d.\n", length);
		memset(buffer, 0, BUFFER_SIZE);
	}
	memset(buffer, 0, BUFFER_SIZE);
	if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
	{
		printf("fd = %d, file: %s recv failed.\n", new_fd, path);
		close(file2_fp);
		return false;
	}
	if(strncmp(buffer, "#successful#", 12))
	{
		printf("fd = %d, file: %s #successful# recv failed.\n", new_fd, path);
		close(file2_fp);
		return false;
	}
	// 关闭文件
	close(file2_fp);
	printf("fd = %d, file: %s send successful.\n", new_fd, path);
	return true;
}

bool senddir(int new_fd, char *path, char * file_name, char *buffer)
{
	char temp_data[100];
	printf("fd = %d, dir: %s begin send...\n", new_fd, path);
	bzero(temp_data, sizeof temp_data);
	sprintf(temp_data, "<file><name>%s</name><size>%ld</size></file>", file_name, (long)1); //要发送的数据部分内容

	struct TCP_STRUCT_DATA struct_data; //数据头
	memset(&struct_data, 0x0, sizeof(struct_data));
	struct_data.m_cmd = GET_DIR;			   //获取文件命令
	struct_data.m_data_len = strlen(temp_data); //数据部分真实的长度

	memset(buffer, 0, BUFFER_SIZE);
	int send_len = sizeof(struct_data);
	memcpy(buffer, &struct_data, send_len);

	//向服务器发送buffer中的数据
	int len = send(new_fd, buffer, send_len, 0); //发数据头部分
	if (len < 0)
	{
		printf("fd = %d, send err...\n", new_fd); //发送失败
		return false;
	}
	memset(buffer, 0, BUFFER_SIZE);
	if((len = recv(new_fd, buffer, 6, 0)) < 0)
	{
		printf("fd = %d, recv err...\n", new_fd); //接收失败
		return false;
	}
	if(strncmp(buffer, "#over#", 6))
	{
		printf("fd = %d, recv data is not #over#...\n", new_fd); //接收失败
		return false;
	}
	len = send(new_fd, temp_data, struct_data.m_data_len, 0);
	if (len < 0)
	{
		printf("fd = %d, send err...\n", new_fd); //发送失败
		return false;
	}
	memset(buffer, 0, BUFFER_SIZE);
	if((len = recv(new_fd, buffer, 6, 0)) < 0)
	{
		printf("fd = %d, recv err...\n", new_fd); //接收失败
		return false;
	}
	if(strncmp(buffer, "#over#", 6))
	{
		printf("fd = %d, recv data is not #over#...\n", new_fd); //接收失败
		return false;
	}
	 //定义一个目录流指针
    DIR *p_dir = NULL;
 
    //定义一个目录结构体指针
    struct dirent *p_dirent = NULL;
 
    //打开目录，返回一个目录流指针指向第一个目录项
    p_dir = opendir(path);
    if(p_dir == NULL)
    {
        printf("opendir error\n");
		return false;
    }
 	bool sendflag = true;
    //循环读取每个目录项, 当返回NULL时读取完毕
    while((p_dirent = readdir(p_dir)) != NULL)
    {	
        if(!strcmp(p_dirent->d_name, ".") || !strcmp(p_dirent->d_name, ".."))
            continue;
		if(!hiddenfile)
        {
            if(p_dirent->d_name[0] == '.')
                continue;
        }
        //备份之前的目录名
        char *backupDirName = NULL;
        //如果目录项仍是一个目录的话，进入目录
		
		//当前目录长度
		int curDirentNameLen = strlen(path)+strlen(p_dirent->d_name) + 2;

		//准备下一级
		backupDirName = (char *)malloc(curDirentNameLen);
		memset(backupDirName, 0, curDirentNameLen);
		memcpy(backupDirName, path, curDirentNameLen);

		strcat(backupDirName, "/");
		strcat(backupDirName, p_dirent->d_name);
		if(p_dirent->d_type == DT_DIR)
            sendflag = senddir(new_fd, backupDirName, p_dirent->d_name, buffer);//递归调用
		else
			sendflag = sendfile(new_fd, backupDirName, p_dirent->d_name, buffer);
			
		free(backupDirName);
		backupDirName = NULL;
		if(!sendflag)
		{
			printf("failed to send %s.\n", path);
			return false;
		}
	}
	
	if (send(new_fd, "#enddir#", 8, MSG_NOSIGNAL) < 0)
	{
		printf("fd = %d, dir: %s send failed.\n", new_fd, path);
		return false;
	}
    closedir(p_dir);
	memset(buffer, 0, BUFFER_SIZE);
	if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
	{
		printf("fd = %d, dir: %s recv failed.\n", new_fd, path);
		return false;
	}
	if(strncmp(buffer, "#dirsuccessful#", 15))
	{
		printf("fd = %d, dir: %s #successful# recv failed.\n", new_fd, path);
		return false;
	}
	printf("fd = %d, dir: %s send successful.\n", new_fd, path);
	return sendflag;
}

bool sendfilepath(int new_fd, char * path, int level, char *buffer, int *hassend)
{
	//定义一个目录流指针
    DIR *p_dir = NULL;
    //定义一个目录结构体指针
    struct dirent *p_dirent = NULL;
 
    //打开目录，返回一个目录流指针指向第一个目录项
    p_dir = opendir(path);
    if(p_dir == NULL)
    {
		memset(buffer, 0, BUFFER_SIZE);
        sprintf(buffer, "opendir: %s error\n", path);
		if(send(new_fd, buffer, strlen(buffer), MSG_NOSIGNAL) < 0)
		{
			printf("fd: %d send error", new_fd);
		}
		return false;
    }
 
    //循环读取每个目录?, 当返回NULL时读取完?
    while((p_dirent = readdir(p_dir)) != NULL)
    {
        //备份之前的目录名
        char *backupDirName = NULL;
		
        if(!strcmp(p_dirent->d_name, ".") || !strcmp(p_dirent->d_name, ".."))
            continue;
        if(!hiddenfile)
        {
            if(p_dirent->d_name[0] == '.')
                continue;
        }
        int i = 0, j = 0;
		memset(buffer, 0, BUFFER_SIZE);
		int istep = 0;
        for(; i < level; i++, j++)
        {
			if(j > 200)
			{
				*hassend += strlen(buffer);
				if(send(new_fd, buffer, strlen(buffer), MSG_NOSIGNAL) < 0)
				{
					printf("fd = %d send error.\n", new_fd);
					return false;
				}
				istep = i;
				j = 0;
			}
			sprintf(buffer + 5* (i - istep), "    |");	
		}
		*hassend += strlen(buffer);
		if(send(new_fd, buffer, strlen(buffer), MSG_NOSIGNAL) < 0)
		{
			printf("fd = %d send error.\n", new_fd);
			return false;
		}
		memset(buffer, 0, BUFFER_SIZE);
        sprintf(buffer,"    %s\n", p_dirent->d_name);
		//printf("%s", buffer);
		if(send(new_fd, buffer, strlen(buffer), MSG_NOSIGNAL) < 0)
		{
			printf("fd = %d send error.\n", new_fd);
			return false;
		}
		*hassend += strlen(buffer);
		if(*hassend > MAX_SENDLENGTH)
		{
			memset(buffer, 0, BUFFER_SIZE);
			if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
			{
				printf("fd = %d, file: %s recv failed.\n", new_fd, path);
				return false;
			}
			if(strncmp(buffer, "#beginsend#", 11))
			{
				printf("fd = %d, file: %s #beginsend# recv failed.\n", new_fd, path);
				return false;
			}
			*hassend = 0;
		}
        //如果目录项仍是一个目录的话，进入目录
        if(p_dirent->d_type == DT_DIR)
        {
            //当前目录长度
            int curDirentNameLen = strlen(path)+strlen(p_dirent->d_name) + 2;
 
            //准备下一个?
            backupDirName = (char *)malloc(curDirentNameLen);
            memset(backupDirName, 0, curDirentNameLen);
            memcpy(backupDirName, path, curDirentNameLen);
 
            strcat(backupDirName, "/");
            strcat(backupDirName, p_dirent->d_name);
            if(!sendfilepath(new_fd, backupDirName, level + 1, buffer, hassend))return false;//递归调用
 
            free(backupDirName);
            backupDirName = NULL;
        }
    }
	
    closedir(p_dir);
	return true;
}


void *net_thread(void * fd)
{
	pthread_detach(pthread_self()); //线程分离
	int new_fd = *((int *)fd);
	int file_fp;
	
	// recv函数接收数据到缓冲区buffer中
	char buffer[BUFFER_SIZE]={0};
	if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
	{
		printf("fd = %d, outfilesize recv failed.\n", new_fd);
		close(new_fd);
		return false;
	}
	if(!strncmp(buffer, "#beginpath#", 11))
	{
		struct TCP_STRUCT_DATA struct_data; //数据头
		memset(&struct_data, 0x0, sizeof(struct_data));
		struct_data.m_cmd = GET_DIR;			   //获取文件命令
		struct_data.m_data_len = outfilesize; //数据部分真实的长度

		memset(buffer, 0, BUFFER_SIZE);
		int send_len = sizeof(struct_data);
		memcpy(buffer, &struct_data, send_len);

		//向服务器发送buffer中的数据
		int len = send(new_fd, buffer, send_len, 0); //发数据头部分
		if (len < 0)
		{
			printf("fd = %d, send err...\n", new_fd); //发送失败
			close(new_fd);
			return NULL;
		}
		memset(buffer, 0, BUFFER_SIZE);
		if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
		{
			printf("fd = %d, outfilesize recv failed.\n", new_fd);
			close(new_fd);
			return NULL;
		}
		if(strncmp(buffer, "#over#", 6))
		{
			printf("fd = %d, #over# recv failed.\n", new_fd);
			close(new_fd);
			return NULL;
		}
		int *hassend = (int *)malloc(sizeof (int));
		*hassend = 0;
		if(!sendfilepath(new_fd, rootfilepath, 0, buffer, hassend))
		{
			printf("fd: %d server recv filepath failed.\n", new_fd);
			close(new_fd);
			free(hassend);
			return NULL;
		}
		free(hassend);
		
		memset(buffer, 0, BUFFER_SIZE);
		if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
		{
			printf("fd = %d, outfilesize recv failed.\n", new_fd);
			close(new_fd);
			return NULL;
		}
		if(strncmp(buffer, "#pathover#", 10))
		{
			printf("fd = %d, #pathover# recv failed.\n", new_fd);
			close(new_fd);
			return NULL;
		}
		close(new_fd);
		return NULL;
	}
	
	if(strlen(buffer) == 0)
	{
		close(new_fd);
		printf("fd = %d client has disconnect.\n", new_fd);
		close(new_fd);
		return NULL;
	}
	// 然后从buffer(缓冲区)拷贝到file_name中
	char file_name[FILE_SIZE];
	memset(file_name, 0, sizeof(file_name));
	strncpy(file_name, buffer, strlen(buffer) > FILE_SIZE ? FILE_SIZE : strlen(buffer));
	memset(buffer, 0, BUFFER_SIZE);

	// 打开文件并读取文件数据
	char sendfile_path[500];
	strcpy(sendfile_path, rootfilepath);
	strcat(sendfile_path, "/");
	strcat(sendfile_path, file_name);
	int sendtype = GET_FILE;
	file_fp = judgefile(sendfile_path);
	if(strlen(file_name) > 1 && file_name[0] == '.' && !hiddenfile)file_fp = -1;
	
	if (file_fp < 0)
	{
		if (send(new_fd, "#file not found#", 16, MSG_NOSIGNAL) == -1)
		{
			printf("fd = %d send error.\n", new_fd);
			close(new_fd);
			return NULL;
		}
	}
	else
	{
		bool recvflag = true;
		switch(file_fp)
		{
			case 1:sendtype = GET_FILE;break;
			case 2:sendtype = GET_DIR;break;
			default:break;
		}
		if(sendtype == GET_DIR)
		{
			recvflag = senddir(new_fd, sendfile_path, file_name, buffer);
		}
		else recvflag = sendfile(new_fd, sendfile_path, file_name, buffer);
		if(!recvflag)
			printf("fd = %d, file or dir: %s send failed!\n", new_fd, file_name);
	}

	close(new_fd);
	printf("fd = %d, has disconnect.\n", new_fd);
}


void server(int server_fd, Popt opt)
{
	int n = 1;
	printf("server begin!\n");
	printf("server listening at %s:%d\n", opt->ip, opt->port);
	printf("file lists:\n");
	outfilesize = TraverseDir(rootfilepath, 0, opt->hidden);
	if(daemon(1, 0) == -1)
	{
		perror("daemon: ");
		exit(EXIT_FAILURE);
	}
	while(1)
	{
		struct sockaddr_in client_addr;
		int size=sizeof(client_addr);
		int new_fd=accept(server_fd,(struct sockaddr *)&client_addr,&size);  //server_fd服务器的socket描述字,&client_addr指向struct sockaddr *的指针,&size指向协议地址长度指针
 
		if(-1==new_fd)
		{
			printf("accept error.\n");
			printf("this is %d accept , and new_fd = %d\n", n++, new_fd);
			continue;       //进行下一次循环
		}
		//inet_ntoa将一个十进制网络字节序转换为点分十进制IP格式的字符串。
		printf("accept client ip：%s:%d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);

		int pthread_id;
		int ret = pthread_create((pthread_t *)&pthread_id,NULL,net_thread,(void *)&new_fd);
		if(-1==ret)
		{
			printf("pthread_create error.\n");
			close(new_fd);
			continue;
		}
		//sleep(1);
	}
	printf("server stop!\n");
	close(server_fd);

}

int main(int argc, char *argv[])
{
	
	//解析参数
	Opt opt;
	memset(&opt , 0, sizeof(Opt));
	opt.show = true;
	strcpy(opt.ip, "localhost");
	opt.port = SERVER_PORT;
	struct option long_options[] = 
	{
			{"listen", required_argument, 0, 'l'},
			{"hidden", no_argument, 0, 'H'},
			{"port", required_argument, 0, 'p'},
			{"help", no_argument, 0, 'h'},
			{0, 0, 0, 0}};
	char shortopts[] = "l:Hp:h";
	if(!parseopt(argc, argv, &opt, long_options, shortopts, true))
		return 0;
	
	//printf("argc = %d\n",argc);
	if(judgefile(argv[argc-1]) != 2)
	{
		printf("please set a dir for server.\n");
		return 0;
	}
	strcpy(rootfilepath, argv[argc-1]);
	int lens = strlen(rootfilepath);
	if(lens > 1 && rootfilepath[lens-1] == '/')
		rootfilepath[lens-1] = '\0';
	hiddenfile = opt.hidden;
    //初始化套接字
	int server_fd=socket(AF_INET,SOCK_STREAM,0);
	if(-1==server_fd)
	{
		perror("socket");
		printf("socket fd has been used up.\n");
		exit(1);
	}
	//绑定端口和ip;
	struct hostent *ht;
	ht=gethostbyname(opt.ip);
	if(NULL==ht)
	{
		printf("failed to start fserver: %s is not a valid address", opt.ip);
		exit(2);
	}
	struct sockaddr_in server_addr;   //struct sockaddr_in为结构体类型 ，server_addr为定义的结构体   
	server_addr.sin_family=AF_INET;   //Internet地址族=AF_INET(IPv4协议) 
	server_addr.sin_port=htons(opt.port);  //将主机字节序转化为网络字节序 ,portnum是端口号
	server_addr.sin_addr = *((struct in_addr *)ht->h_addr_list[0]);//IP地址
	if(-1==bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr)))  //套接字与端口绑定
	{
		perror("bind");
		printf("failed to start fserver: %s:%d already in use\n", opt.ip, opt.port);
		exit(3);
	}
	//开启监听
	if(-1==listen(server_fd, MAX_CONNETCION_COUNT)) //5是最大连接数，指服务器最多连接5个用户
	{
		perror("listen");
		exit(4);
	}
	
	server(server_fd, &opt);
	return 0;
 
}
 
 
 
